package com.kauto.gps;

import com.kauto.gps.config.GpsFtpProperties;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.net.ftp.*;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

@Slf4j
@Component
public class GpsFtpManager {
    String Control_Encoding = "UTF-8";
    private FTPClient client = null;
    public static final String ANONYMOUS_USER = "anonymous";

    public void connect() throws Exception {
        if (client == null) {
            client = new FTPClient();
        }
        if (client.isConnected()) {
            return;
        }
        //client.setDefaultTimeout(10 * 1000);
        /*
        client.setConnectTimeout(10 * 1000);
        client.setSoTimeout(10 * 1000);
        client.setDataTimeout(10 * 1000);
        */
        client.setControlEncoding(Control_Encoding);
        try {
            client.connect(GpsFtpProperties.ip, GpsFtpProperties.port);
            if (GpsFtpProperties.user == null || "".equals(GpsFtpProperties.user)) {
                client.login(ANONYMOUS_USER, ANONYMOUS_USER);
            } else {
                client.login(GpsFtpProperties.user, GpsFtpProperties.pwd);
            }
            int reply = client.getReplyCode();
            if (!FTPReply.isPositiveCompletion(reply)) {
                client.disconnect();
                throw new Exception("connection FTP failed!");
            }

            FTPClientConfig config = new FTPClientConfig(client.getSystemType().split(" ")[0]);
            config.setServerLanguageCode("zh");
            client.configure(config);
            // 允许启用/禁用远程主机连接验证
            client.setRemoteVerificationEnabled(false);
            // 使用被动模式设为默认
            client.enterLocalPassiveMode();
            client.setFileType(FTP.BINARY_FILE_TYPE);
            // 设置模式
            client.setFileTransferMode(FTP.STREAM_TRANSFER_MODE);

        } catch (IOException e) {
            log.error(e.getMessage(), e);
            throw new Exception("connection FTP failed! " + e);
        }
    }

    public void close() {
        if (client != null && client.isConnected()) {
            try {
                client.logout();
                client.disconnect();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    public List<FTPFile> list() {
        return list(GpsFtpProperties.rootDir);
    }

    public List<FTPFile> list(String path) {
        List<FTPFile> list = new ArrayList<>();
        try {
            FTPFile ff[] = client.listFiles(path);
            if (ff != null && ff.length > 0) {
                Collections.addAll(list, ff);
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return list;
    }

    public boolean cd(String path) {
        return cd(path, false);
    }

    public boolean cd(String path, boolean forcedCreate) {
        boolean ok = false;
        try {
            if (path != null && !"".equals(path)) {
                ok = client.changeWorkingDirectory(path);
                if (!ok) {
                    if (forcedCreate) {
                        StringTokenizer token = new StringTokenizer(path, "\\//");
                        while (token.hasMoreTokens()) {
                            String npath = token.nextToken();
                            client.makeDirectory(npath);
                            ok = client.changeWorkingDirectory(npath);
                            if (!ok) {
                                break;
                            }
                        }
                    }
                }
            }
            return ok;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    public boolean mkdir(String path) {
        boolean ok = false;
        try {
            if (path != null && !"".equals(path)) {
                ok = client.changeWorkingDirectory(path);
                if (!ok) {
                    StringTokenizer token = new StringTokenizer(path, "\\//");
                    while (token.hasMoreTokens()) {
                        String npath = token.nextToken();
                        client.makeDirectory(npath);
                        ok = client.changeWorkingDirectory(npath);
                        if (!ok) {
                            break;
                        }
                    }
                }
            }
            return ok;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return false;
    }

    public boolean deleteDirectory(String path) {
        boolean flag = false;
        try {
            flag = client.removeDirectory(path);
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        }
        return flag;
    }

    public boolean deleteFile(String path) {
        boolean flag = false;
        try {
            flag = client.deleteFile(path);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return flag;
    }

    public String getSize(String path) throws IOException {
        return client.getSize(path);
    }

    public String getStatus(String path) throws IOException {
        return client.getStatus(path);
    }


    //下载开启本地被动模式
    public void enterLocalPassiveMode() {
        client.enterLocalPassiveMode();
    }

    //上传开启远程被动传输模式
    public boolean enterRemotePassiveMode() throws IOException {
        return client.enterRemotePassiveMode();
    }

    public boolean upload(File localFile) {
        return this.upload(localFile, null);
    }

    public boolean upload(File localFile, String reName) {
        return upload(localFile, reName, null);
    }

    public boolean upload(File localFile, String reName, String remoteDir) {
        if (StringUtils.hasText(remoteDir)) {
            cd(remoteDir);
        }
        boolean flag = false;
        String targetName = reName;
        // 设置上传后文件名
        if (!StringUtils.hasText(reName)) {
            targetName = localFile.getName();
        }
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(localFile);
            client.setControlEncoding(Control_Encoding);
            client.setFileType(FTPClient.BINARY_FILE_TYPE);
            boolean ok = client.storeFile(targetName, fis);
            if (ok) {
                flag = true;
            }
        } catch (IOException e) {
            log.error(e.getMessage(), e);
        } finally {
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
        return flag;
    }

    public void getFiles(String path, Map<String, FTPFile> ftpFileMap) {
        List<FTPFile> list = list(path);
        for (FTPFile ftpFile : list) {
            String temp = path + "/" + ftpFile.getName();
            if (ftpFile.isFile()) {
                ftpFileMap.put(temp, ftpFile);
            } else {
                getFiles(temp, ftpFileMap);
            }
        }
    }

    public boolean rename(String oldName, String newName) throws IOException {
        return client.rename(oldName, newName);
    }


    public boolean download(String ftpFileName, String savePath) {
        boolean flag = false;

        File dir = new File(savePath);

        if (!dir.exists()) {
            dir.mkdirs();
        }

        FileOutputStream fos = null;
        try {
            String saveFile = dir.getAbsolutePath() + File.separator + ftpFileName;
            fos = new FileOutputStream(saveFile);
            boolean ok = client.retrieveFile(ftpFileName, fos);
            if (ok) {
                flag = true;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return flag;
    }
}
